#include "Sheet_impl.h"
#include "Entry_impl.h"


Sheet* CreateSheet(const char* memdata, int size)
{
	SheetImpl* impl = new SheetImpl(memdata,size);
	if(impl->IsLoaded())
		return impl;
	else
		delete impl;
	return 0;
}

SheetImpl::SheetImpl(const char* memdata, int size)
	: m_hasID(false)
	, m_isLoaded(false)
	, m_idColumn(-1)
{
	if(ParseSheetData(memdata, size))
	{
		//do something.
		m_isLoaded = true;
	}
	else
	{
		m_isLoaded = false;
	}
}

SheetImpl::~SheetImpl()
{
	for(int i =0; i != m_titles.size(); i++)
	{
		delete m_titles[i];
	}
	m_titles.clear();

	EMIter itBeg = m_entrys.begin();
	EMIter itEnd = m_entrys.end();
	while(itBeg != itEnd)
	{
		Entry* entry = itBeg->second;
		delete entry;
		itBeg++;
	}
	m_entrys.clear();
}

Entry* SheetImpl::GetEntry(int id)
{
	EMIter itFind = m_entrys.find(id);
	if(itFind != m_entrys.end())
		return itFind->second;
	return  0;
}
int SheetImpl::GetColumnCount()
{
	return m_titles.size();
}

int SheetImpl::GetType(int column)
{
	if(column < 0 || column >= GetColumnCount())
		return 0;

	return m_titles[column]->type;
}
const char*	SheetImpl::GetTitle(int column)
{
	if(column < 0 || column >= GetColumnCount())
		return 0;

	return m_titles[column]->name.c_str();
}
int SheetImpl::GetColumn(const char* title)
{
	/*
	TMItor itFind = m_titleMap.find(title);
	if(itFind != m_titleMap.end())
		return itFind->second;
	*/
	for(int i = 0; i != GetColumnCount(); i++)
	{
		Title* t = m_titles[i];
		std::string t2 = title;
		if(t->name == t2)
			return i;
	}
	return  -1;
}

int SheetImpl::GetIDColumn()
{
	if(m_hasID)
		return m_idColumn;
	else
		return -1;
}
bool SheetImpl::IsLoaded()
{
	return m_isLoaded;
}

namespace{
	//return nextline
	//current line will not be included \n\r\0
	//trim blanks with tails and head
	const char* GetLine(const char * mem, int& totalSize, int& lineSize, const char*& curline)
	{
		if(totalSize == 0)
			goto LAB_FILE_END;

		curline = mem;
		lineSize = 0;
		
		while(1)
		{
			//blanks returns
			if(totalSize >= 1 && (*curline==' ' || *curline == '\t' || *curline == '\n' || *curline == '\r'))
			{
				curline++;
				if(--totalSize == 0)
					goto LAB_FILE_END;
			}
			//test comment.
			else if(totalSize >= 2 && ((curline[0] == '\"') || (curline[0] == '/' && curline[1] == '/')))
			{
				//isComment = true;
				while(*curline != '\n' && *curline != '\r' && *curline != '\0')
				{
					curline++;
					//file end, return
					if(--totalSize == 0)
						goto LAB_FILE_END;
				}
			}
			else
			{
				break;
			}
		}
		
		//scan content.
		while(curline[lineSize] != '\n' && curline[lineSize] != '\r' && curline[lineSize] != '\0' && totalSize != 0)
		{
			lineSize++;
			--totalSize;
		}
		if(curline[lineSize] == '\0')
		{
			totalSize = 0;
			return 0;
		}
		else if(totalSize > 0)
		{
			totalSize -= 1;
			lineSize--;
			return curline + (lineSize+2);
		}
		else
		{
			return 0;
		}

LAB_FILE_END:
		lineSize = 0;
		curline = 0;
		totalSize = 0;
		return 0;
	}


}
bool SheetImpl::ParseSheetData(const char* memdata, int size)
{
	
	int lineSize = 0;
	int totalSize = size;
	const char* nextline;
	const char* curline;
	int lineCount = 0;
	//title
	nextline = GetLine(memdata, totalSize, lineSize, curline);
	if(curline == 0)
		return false;
	ParseSheetTitle(curline, lineSize);


	//entrys
	while(nextline != 0)
	{
		nextline = GetLine(nextline, totalSize, lineSize, curline);
		if(curline == 0)
			break;
		ParseSheetEntry(curline, lineSize, lineCount++);
	}
	return true;
}


bool SheetImpl::ParseSheetTitle(const char* memdata, int size)
{
	int cur = 0;
	int tbeg = 0;
	int column = 0;
	while(size >= cur)
	{
		while(memdata[cur] != '|' && size >= cur)
			cur++;

		if(size < cur)
		{
			break;
		}
		else if(cur != tbeg)	//create title
		{
			Title* title = new Title;
			title->name = std::string(memdata+tbeg, memdata+(cur));
			switch(memdata[cur + 1])
			{
			case 'I':
			case 'i':
				title->type = CT_ID;
				m_hasID = true;
				m_idColumn = column;
				break;
			case 'N':
			case 'n':
				title->type = CT_INT;
				break;
			case 'F':
			case 'f':
				title->type = CT_FLOAT;
				break;
			case 'S':
			case 's':
				title->type = CT_STR;
				break;
			}
			column++;
			cur+=2;
			m_titles.push_back(title);
			//m_titleMap.insert(std::map_pair<std::string, Title*>(title->name, title));
			
			//filter blanks
			while((memdata[cur] == ' ' || memdata[cur] == '\t')
				&& size >= cur)
				cur++;
			if(size < cur)
			{
				break;
			}
			else
			{
				tbeg = cur;
			}
		}
	}
	return true;
}
bool SheetImpl::ParseSheetEntry(const char* memdata, int size, int line)
{
	EntryImpl* entry = new EntryImpl(this, memdata, size);
	if(entry->IsLoaded())
	{
		int id = m_hasID ? entry->GetID() : line;
		m_entrys.insert(std::make_pair<int, Entry*>(id, entry));
		return true;
	}
	else
	{
		delete entry;
		return false;
	}
	
}

EntryIter* SheetImpl::GetFirst()
{
	return new EntryIterImpl(m_entrys.begin(), m_entrys.end());
}

EntryIter* SheetImpl::GetLast()
{
	return new EntryIterImpl(m_entrys.end());
}